package com.zmc.third.application.factory.service.alipay;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.zmc.common.annotation.PreventRepeatSubmit;
import com.zmc.common.constant.QueueConstant;
import com.zmc.common.domain.resp.order.RefundResp;
import com.zmc.common.domain.resp.order.ThirdPayResp;
import com.zmc.common.domain.vo.order.OrderPayInfo;
import com.zmc.common.domain.vo.third.PayRefundVo;
import com.zmc.common.exception.base.BusinessException;
import com.zmc.common.utils.JsonUtils;
import com.zmc.common.utils.R;
import com.zmc.common.utils.StringUtils;
import com.zmc.common.utils.redis.RedisCache;
import com.zmc.common.utils.uuid.IdUtils;
import com.zmc.common.utils.uuid.SnowflakeIdUtils;
import com.zmc.common.vo.OrderPayInfoVo;
import com.zmc.third.application.factory.PayTypeFactory;
import com.zmc.third.application.factory.service.PayService;
import com.zmc.third.feign.OrderFeignService;
import com.zmc.third.infrastructure.bean.resp.PayNotifyResp;
import com.zmc.third.infrastructure.bean.resp.PayQueryResp;
import com.zmc.third.infrastructure.constant.StateCodeConstant;
import com.zmc.third.infrastructure.entity.PaySettingEntity;
import com.zmc.third.infrastructure.entity.PaymentInfoEntity;
import com.zmc.third.infrastructure.entity.RefundInfoEntity;
import com.zmc.third.infrastructure.enums.PayEnum;
import com.zmc.third.infrastructure.service.PaySettingService;
import com.zmc.third.infrastructure.service.PaymentInfoService;
import com.zmc.third.infrastructure.service.RefundInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author：zmc
 * @Date：2025/1/10 14:57
 */

@Component
public class AbstractAlipayService implements PayService {

    public static final Logger log = LoggerFactory.getLogger(AbstractAlipayService.class);

    @Autowired
    private PaySettingService paySettingService;
    @Autowired
    private OrderFeignService orderFeignService;
    @Autowired
    private PaymentInfoService paymentInfoService;
    @Autowired
    private RefundInfoService refundInfoService;
    @Autowired
    private PayTypeFactory payTypeFactory;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    @Transactional
    @PreventRepeatSubmit(key = "#orderNo", openSpEL = true)
    public R getPayChannel(String orderNo) {
        PaymentInfoEntity payment = paymentInfoService.getByOrderSn(orderNo);
        //生成支付流水
        if (payment == null) {
            OrderPayInfoVo vo = new OrderPayInfoVo();
            vo.setOrderNo(orderNo);
            OrderPayInfo orderInfo = JsonUtils.transToClass(orderFeignService.getPayInfoByOrderNo(vo), OrderPayInfo.class);
            payment = new PaymentInfoEntity();
            payment.setMerchantId(orderInfo.getMerchantId());
            payment.setTradeSn(SnowflakeIdUtils.nextSnowFlackId());
            payment.setTotalAmount(orderInfo.getTotalAmount());
            payment.setPaymentStatus(PayEnum.PAY_INIT.getCode());
            payment.setSubject(orderInfo.getSubject());
            paymentInfoService.save(payment);
        }
        PaySettingEntity paySetting = paySettingService.getPaySetting();
        AlipayClient alipayClient = getAlipayClient(paySetting);
        String payNotifyUrl = paySetting.getNotifyGatewayUrl() + paySetting.getPayNotifyUrl();
        String returnUrl = paySetting.getNotifyGatewayUrl() + paySetting.getReturnUrl();
        return payTypeFactory.getPayType(paySetting.getPayType().toString()).requestPayChannel(alipayClient, payment, payNotifyUrl, returnUrl);
    }

    @Override
    @Transactional
    public PayNotifyResp notify(Map<String, String> params) {
        PayNotifyResp resp = new PayNotifyResp();
        PaySettingEntity paySetting = paySettingService.getPaySetting();
        try {
            boolean flag = AlipaySignature.rsaCheckV1(params, paySetting.getAlipayPublicKey(), "utf-8", "RSA2");
            if (flag) {
                if (params.get("trade_status").equals(PayEnum.ALIPAY_STATUS_TRADE_SUCCESS.getCode()) || params.get("trade_status").equals(PayEnum.ALIPAY_STATUS_TRADE_FINISHED.getCode())) {
                    String tradeNo = params.get("out_trade_no");
                    if (StringUtils.isEmpty(tradeNo)) {
                        log.error("支付宝回调异常,result:{}", JSON.toJSONString(params));
                        throw new BusinessException("支付宝回调：订单号不能为空");
                    }
                    String uuid = IdUtils.fastUUID();
                    //支付幂等性处理
                    boolean lock = redisCache.setCacheIfAbsent(tradeNo, uuid, 30, TimeUnit.SECONDS);
                    try {
                        if (lock) {
                            PaymentInfoEntity infoEntity = paymentInfoService.getByTradeSn(tradeNo);
                            if (infoEntity == null) {
                                log.error("支付宝回调异常，订单交易号不存在，tradeNo:{}", JSON.toJSONString(params));
                                throw new BusinessException("支付宝回调异常，订单交易号不存在");
                            }
                            if (infoEntity.getPaymentStatus().equals(PayEnum.PAY_SUCCESS.getCode())) {
                                log.info("当前订单已支付完成，tradeNo:{}", JSON.toJSONString(params));
                            } else {
                                infoEntity.setAlipayTradeNo(params.get("trade_no"));
                                infoEntity.setPaymentStatus(PayEnum.PAY_SUCCESS.getCode());
                                infoEntity.setCallbackContent(JSON.toJSONString(params));
                                paymentInfoService.paySuccessUpdateInfo(tradeNo, params.get("trade_no"), JSON.toJSONString(params));
                                //支付成功，订单更新完成，准备分单
                                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                                    @Override
                                    public void afterCommit() {
                                        ThirdPayResp payResp = new ThirdPayResp();
                                        payResp.setType(PayEnum.PAY_WAY_ALIPAY.getCode());
                                        payResp.setOrderSn(infoEntity.getOrderSn());
                                        payResp.setTradeSn(infoEntity.getTradeSn());
                                        rabbitTemplate.convertAndSend(QueueConstant.SPLIT_ORDER_EXCHANGE, QueueConstant.SPLIT_ORDER_QUEUE_ROUTING_KEY, payResp);
                                    }
                                });
                            }
                        }
                    } finally {
                        String cacheObject = redisCache.getCacheObject(tradeNo);
                        if (cacheObject.equals(uuid)) {
                            redisCache.deleteObject(tradeNo);
                        }
                    }
                    resp.setResult(StateCodeConstant.ALIPAY_NOTIFY_RETURN_SUCCESS);
                } else {
                    log.warn("支付宝回调：支付失败，result:{}", JSON.toJSONString(params));
                    resp.setResult(StateCodeConstant.ALIPAY_NOTIFY_RETURN_FAIL);
                }
            } else {
                //验签失败
                log.error("支付宝回调：验签失败,result:{}", JSON.toJSONString(params));
                throw new BusinessException("支付宝回调：验签失败");
            }
        } catch (AlipayApiException e) {
            throw new BusinessException("支付宝回调异常");
        }
        return resp;
    }


    @Override
    public PayQueryResp queryPayResult(String orderNo) {
        PayQueryResp resp = new PayQueryResp();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(orderNo);
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizModel(model);
        PaySettingEntity paySetting = paySettingService.getPaySetting();
        AlipayClient alipayClient = getAlipayClient(paySetting);
        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            JSONObject body = JSONObject.parseObject(response.getBody());
            JSONObject alipayResponse = (JSONObject) body.get("alipay_trade_query_response");
            String code = alipayResponse.get("code").toString();
            //交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）
            if (code.equals(PayEnum.ALIPAY_NOT_EXIST.getCode())) {
                //订单未创建（用户未扫码）
                resp.setCode(PayEnum.PAY_QUERY_RETURN_NOT_EXIST.getCode());
                resp.setMsg(PayEnum.PAY_QUERY_RETURN_NOT_EXIST.getMsg());
                return resp;
            }
            String tradeStatus = alipayResponse.get("trade_status").toString();
            if (code.equals(PayEnum.ALIPAY_CREATE.getCode()) && tradeStatus.equals(PayEnum.ALIPAY_STATUS_WAIT_BUYER_PAY.getCode())) {
                //订单已经创建但未支付（用户扫码后但是未支付）
                resp.setCode(PayEnum.PAY_QUERY_RETURN_NOT_PAY.getCode());
                resp.setMsg(PayEnum.PAY_QUERY_RETURN_NOT_PAY.getMsg());
            } else if (code.equals(PayEnum.ALIPAY_CREATE.getCode()) && (tradeStatus.equals(PayEnum.ALIPAY_STATUS_TRADE_SUCCESS.getCode()) || tradeStatus.equals(PayEnum.ALIPAY_STATUS_TRADE_FINISHED.getCode()))) {
                //判断code是否等于”10000“ 并且 tradeStatus等于TRADE_SUCCESS（交易支付成功）或者 trade_status等于TRADE_FINISHED（交易结束，不可退款）
                //订单已支付（用户扫码完成并且支付成功之后）
                resp.setCode(PayEnum.PAY_QUERY_RETURN_SUCCESS.getCode());
                resp.setMsg(PayEnum.PAY_QUERY_RETURN_SUCCESS.getMsg());
            } else {
                resp.setCode(PayEnum.PAY_QUERY_RETURN_UNKNOWN.getCode());
                resp.setMsg(PayEnum.PAY_QUERY_RETURN_UNKNOWN.getMsg());
            }
        } catch (AlipayApiException e) {
            log.error("alipay支付-查询异常,orderNo:{}", orderNo, e);
            throw new RuntimeException(e);
        }
        return null;
    }

    @Override
    @Transactional
    @PreventRepeatSubmit(key = "#vo.orderSn", openSpEL = true)
    public RefundResp refund(PayRefundVo vo) {
        RefundResp resp = new RefundResp();
        resp.setSuccess(false);
        String orderSn = vo.getOrderSn();
        PaymentInfoEntity paymentInfo = paymentInfoService.getByOrderSn(orderSn);
        //查询是否有支付记录
        if (paymentInfo == null || !paymentInfo.getPaymentStatus().equals(PayEnum.PAY_SUCCESS.getCode())) {
            throw new BusinessException("当前订单未进行支付，无法退款");
        }
        RefundInfoEntity refundInfo = refundInfoService.getByOrderSn(orderSn);
        if (refundInfo != null) {
            if (refundInfo.getRefundStatus().equals(PayEnum.REFUND_SUCCESS.getCode())) {
                throw new BusinessException("当前订单已退款");
            } else if (refundInfo.getRefundStatus().equals(PayEnum.REFUND_PROGRESS.getCode())){
                throw new BusinessException("当前订单正在退款中");
            }
        } else {
            refundInfo = new RefundInfoEntity();
            refundInfo.setOrderSn(orderSn);
            refundInfo.setTradeSn(paymentInfo.getTradeSn());
            refundInfo.setRefundSn(SnowflakeIdUtils.nextSnowFlackId());
            refundInfo.setTotalAmount(vo.getAmount());
            refundInfo.setRefundReason(vo.getRefundReason());
            refundInfo.setCreateTime(LocalDateTime.now());
            refundInfo.setRefundStatus(PayEnum.REFUND_INIT.getCode());
            refundInfoService.save(refundInfo);
        }
        //退款金额对账
        if (paymentInfo.getTotalAmount().compareTo(vo.getAmount()) < 0) {
            throw new BusinessException("对账金额异常，无法退款");
        }
        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
        //商户订单号
        refundModel.setOutTradeNo(paymentInfo.getTradeSn());
        //退款请求号(部分退款时，每次请求必须唯一，否则一个商户订单号会发起多比退款)
        refundModel.setOutRequestNo(refundInfo.getRefundSn());
        //退款金额
        refundModel.setRefundAmount(refundInfo.getTotalAmount().toString());
        //退款描述
        refundModel.setRefundReason(vo.getRefundReason());
        refundRequest.setBizModel(refundModel);
        //退款回调通知
        PaySettingEntity paySetting = paySettingService.getPaySetting();
        refundRequest.setNotifyUrl(paySetting.getRefundNotifyUrl());
        AlipayTradeRefundResponse response = null;
        try {
            response = getAlipayClient(paySetting).execute(refundRequest);
            log.info("支付宝申请退款接口，返回参数：{}", response.getBody());
            JSONObject jsonObject = JSON.parseObject(response.getBody());
            JSONObject refundResponse = jsonObject.getJSONObject("alipay_trade_refund_response");
            if (!response.isSuccess()) {
                log.error("支付宝交易退款接口调用失败，单号：{}", orderSn);
            } else {
                String isRefund = refundResponse.getString("fund_change");
                refundInfo.setCallbackContent(response.getBody());
                /*
                 * 退款成功判断说明：接口返回fund_change=Y为退款成功，fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态。
                 * 详见退款成功判断指导。注意，接口中code=10000，仅代表本次退款请求成功，不代表退款成功
                 */
                if ("Y".equals(isRefund)) {
                    resp.setSuccess(true);
                    refundInfo.setRefundStatus(PayEnum.REFUND_SUCCESS.getCode());
                } else {//退款中，查询确认
                    refundInfo.setRefundStatus(PayEnum.REFUND_PROGRESS.getCode());
                    //todo 放入mq查询退款结果
                }
                refundInfoService.updateStatusByOrderSnAndRefundSn(refundInfo.getOrderSn(), refundInfo.getRefundStatus());
            }
        } catch (AlipayApiException e) {
            log.error("支付宝申请退款接口,调用异常", e);
        }
        return resp;
    }

    private AlipayClient getAlipayClient(PaySettingEntity paySetting) {
        return new DefaultAlipayClient(paySetting.getGatewayUrl(), paySetting.getAppId(), paySetting.getPrivateKey(), "json", "UTF-8", paySetting.getAlipayPublicKey(), paySetting.getSignType());
    }
}
